﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Web;
using System.Runtime.Serialization;
using ServeurRSS_1_0.Source.ExceptionRss;

namespace ServeurRSS_1_0.Source.Model
{

    [DataContract]
    public class DataRss
    {
        [DataMember]
        public System.Guid id
        { get; set; }

        [DataMember]
        public string guid
        { get; set; }

        [DataMember]
        public string name
        { get; set; }

        [DataMember]
        public string  url
        { get; set; }

        [DataMember]
        public string link
        { get; set; }

        [DataMember]
        public DateTime? last_importation
        { get; set; }

    }

    public class RssModel : DirectoryModel
    {
        public delegate string insertRssInDBDelegate(XElement doc, System.Guid idRss, string url);
        public delegate string insertRssItemInDBDelegate(XElement doc, System.Guid idRss);
        public delegate XElement checkFeedDelegate(System.Guid idRss, string url);
  

        public insertRssInDBDelegate insertRss
        { get; set; }
        public insertRssItemInDBDelegate insertItem
        { get; set; }
        public checkFeedDelegate checkFeed
        { get; set; }

        public RssModel()
        {
            insertRss = null;
            insertItem = null;
            checkFeed = null;

        }
        
        #region FeedIntegration
        #region toolsMethods

        public string integrateRss(System.Guid idRss, string url = "", checkFeedDelegate checkMethod = null)
        {
            if (checkFeed == null && checkMethod == null)
                throw (new Exception("No checking method choosen in integrateRss"));
            if (checkMethod != null)
                checkFeed = checkMethod;
            if (idRss.Equals(System.Guid.Empty) && url.Equals(""))
                throw (new Exception("No checking rss choosen in integrateRss"));
            string res;
 
            if (idRss.Equals(System.Guid.Empty))
                    idRss = Guid.NewGuid();
                XElement rssFeed = checkFeed(idRss, url);
                // prend le feed et l'id du feed dans la base


                if (insertRss == null)
                    res = "  Aucun parser de Rss selectionné pour la delegate insertRss dans integrateRss"; 
                else
                    res = "Insert Rss: " + insertRss(rssFeed, idRss, url);
                if (insertItem == null)
                    throw (new Exception("Aucun parser d'item selectionné pour la delegate insertItem dans integrateRss"));
      
                res += "Insert Item : " + insertItem(rssFeed, idRss);

                return res;
        }

        #endregion

        #region parsingDelegate

        public string insertBasicRss(XElement doc, System.Guid idRss, string url)
        {
            try 
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();
                var feed = from elem in doc.Descendants("channel")
                           select new Rss
                           {
                               id = idRss,
//                               guid = elem.Element("guid").Value,
                               last_date_importation =  DateTime.Now,
                               name = elem.Element("title").Value,
                               url = url,
                               link = elem.Element("link").Value
                           };
                if (feed != null) 
                    db.Rsses.InsertAllOnSubmit<Rss>(feed);
                db.SubmitChanges();
            }
            catch (Exception e)
            {
                throw new Exception("exception dans insertBasicRss " + e.Message, e);
            }
            return "OK";
        }

        public string insertBasicItemRss(XElement doc, System.Guid idRss)
        {
            try
            {
                ItemModel mod = new ItemModel();
                List<DataItem> feed = (from elem in doc.Elements("item")
                            select new DataItem
                           {
                               id = Guid.NewGuid(),
                               titre = elem.Element("title").Value,
                               description = elem.Element("description").Value,
                               date = elem.Element("pubDate").Value.Length > 0 ? Convert.ToDateTime(elem.Element("pubDate").Value) : DateTime.Now,
                               //                               guid = elem.Element("guid").Value,
                               link = elem.Element("link").Value,
                               fk_id_Rss = idRss,
                               media = mod.getListDataImage((from med in elem.Descendants("enclosure").DefaultIfEmpty()
                                        select new Image
                                        {
                                            id = Guid.NewGuid(),
                                            url = med.Attribute("url").Value,
                                            type = med.Attribute("type").Value,
                                            link = ""
                                        }).ToList<Image>())
                           }).ToList<DataItem>();
                ItemModel modelItem = new ItemModel();
                return (modelItem.insertListItemInDB(feed));
            }
            catch (Exception e)
            {
                throw new Exception("exception dans insertBasicItemRss " + e.Message, e);
            }
        }

        private XElement getUnknownItem(XDocument doc, System.Guid id)
        { 
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();

                DateTime tmp = (from elem in db.Items
                                     where elem.fk_id_Rss == id
                                     orderby elem.date descending
                                     select elem.date).FirstOrDefault();
                if (tmp == null)
                    throw new Exception("Cannot obtain lastItemm published in DB");
                DateTime lastItemInDB = (DateTime)tmp;

                XElement toto = doc.Root.Element("channel").Element("item");
                string totoDate = toto.Element("pubDate").Value;
                DateTime realTotoDate = Convert.ToDateTime(totoDate);
                IEnumerable<XElement> elements = from elem in doc.Root.Element("channel").Elements("item")
                                                where lastItemInDB < Convert.ToDateTime(elem.Element("pubDate").Value)
                                                orderby elem.Element("pubDate").Value ascending
                                                select elem;
                XElement res = new XElement("channel");
                foreach (XElement el in elements)
                    res.Add(new XElement(el));
                return res;

            }
            catch (Exception e)
            {
                throw (new Exception("Erreur GetUnkwonItem : " + e.Message, e));
            }
        }

        #endregion

        #region checkingDelegate

        public System.Guid? checkUrlRss(string urlRss)
        {
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();

                Rss rss = db.Rsses.SingleOrDefault(p => p.url == urlRss);
                if (rss != null)
                    return rss.id;
                return null;
            }
            catch (Exception e)
            {
                return null;
            }
        }

        //TODO erifier si d'autre version passe pour cet integrate
        public  XElement checkFeedIntegrate(System.Guid id, string url)
        {
            //            Attention verifier si c'est un update ou une creaton de flux dans la base et importer la bonne delegate 
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();

                Rss res = db.Rsses.FirstOrDefault(r => r.id == id || r.url == url);
                if (res != null)
                    throw (new Exception("rss already exit"));
                XDocument doc =  XDocument.Load(url);
                double version = Convert.ToDouble(doc.Element("rss").Attribute("version").Value);
                if (version == 2.0)
                {
                    insertRss = insertBasicRss;
                    insertItem = insertBasicItemRss;
                }
                else
                    throw (new Exception("version non supporté"));
                return doc.Element("rss");
            }
            catch (Exception e)
            {
                throw (new Exception("Erreur checkFeedIntegrate, " + e.Message, e));
            }
        }

        public XElement checkFeedUpdate(System.Guid id, string url)
        {
            //            Attention verifier si c'est un update ou une creaton de flux dans la base et importer la bonne delegate 
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();

                Rss res = db.Rsses.FirstOrDefault(r => r.id == id || r.url == url);
                if (res == null)
                    throw (new ExceptionUpdate("rss doesn't exit"));
                XDocument doc = XDocument.Load(res.url);
                double version = Convert.ToDouble(doc.Element("rss").Attribute("version").Value);
                if (version == 2.0)
                {
                    insertRss = null;
                    insertItem = insertBasicItemRss;
                }
                else
                    throw (new ExceptionUpdate("version non supporté"));

                DateTime? lastUp = res.last_date_importation;
                if (lastUp != null)
                    if (((DateTime)lastUp).Date.Equals(DateTime.Now.Date))
                        throw (new ExceptionUpdate("No need to udate feed"));
                 return getUnknownItem(doc, id);
            }
            catch (Exception e)
            {
                throw (new ExceptionUpdate("Erreur checkFeedUpdate, " + e.Message, e));
            }
        }

        #endregion

        #endregion

        #region userRssManagement

        public string addRssToUserWithDir(System.Guid dirId, System.Guid rssId, bool own)
        {
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();
                System.Guid? idUser = db.DirectoriesUsers.FirstOrDefault(p => p.id == dirId).fk_id_Directories_User;
                if (idUser == null)
                    throw (new Exception("Erreur addRssToUser, idUser == null"));
                return addRssToUser((System.Guid)idUser, rssId, own);  
            }
            catch (Exception e)
            {
                
                throw (new Exception("Erreur addRssToUser, " + e.Message, e));
            }
        }

        public string addRssToUser(System.Guid userId, System.Guid rssId, bool own)
        {
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();
                if (db.UserRsses.FirstOrDefault(p => p.fk_id_User == userId && rssId == p.fk_id_Rss) != null)
                    return "Already add One time";
                UserRss abonnement = new UserRss
                {
                    fk_id_User = (System.Guid?) userId,
                    fk_id_Rss = (System.Guid?) rssId,
                    owner = own
                };
                db.UserRsses.InsertOnSubmit(abonnement);
                db.SubmitChanges();
                return "OK";
            }
            catch (Exception e)
            {
                
                throw(new Exception("Erreur addRssToUser, " + e.Message, e));
            }
        }

        public string removeRssFromUser(System.Guid? userId, System.Guid? rssId)
        {
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();
                
                db.UserRsses.DeleteOnSubmit(db.UserRsses.FirstOrDefault(p => p.fk_id_Rss == rssId && p.fk_id_User == userId));
                db.SubmitChanges();
                return "OK";
            }
            catch (Exception e)
            {

                throw (new Exception("Erreur addRssToUser, " + e.Message, e));
            }
        }
        
        #endregion

        #region getterMethods


        public List<Rss> getRssUser(System.Guid idUser, bool? owner)
        {

            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();
                if (owner == null)
                    return (from elem in db.Rsses
                            from userrss in db.UserRsses
                            where elem.id == userrss.fk_id_Rss && userrss.fk_id_User == idUser
                            select elem).ToList<Rss>();

                return (from elem in db.Rsses
                        from userrss in db.UserRsses
                        where elem.id == userrss.fk_id_Rss && userrss.fk_id_User == idUser && userrss.owner == owner
                        select elem).ToList<Rss>();
            }
            catch (Exception e)
            {

                throw (new Exception("Erreur getRssUser : " + e.Message, e));
            }
          
        }

        public List<Rss> getRssForDir(System.Guid idDir)
        {
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();
                return (from elem in db.Rsses
                        from dir in db.DirectoriesRsses
                        where elem.id == dir.fk_id_Rss && dir.fk_id_Directories == idDir
                        select elem).ToList<Rss>();
            }
            catch (Exception e)
            {

                throw (new Exception("Erreur getRssUser : " + e.Message, e));
            }
          
        }

        public RssTree getDirRssTree(DirectoriesUser userDir)
        {
            try
            {
                if (userDir.fk_id_Directories_User == null)
                    return null;
                return getAll((System.Guid)userDir.fk_id_Directories_User);
            }
            catch (Exception e)
            {
                
                return  null;
            }
        }

        public RssTree getUserFullTree(System.Guid idUser)
        {
            return  getDirRssTree(getRootDirectoryForUser(idUser));
        }

        public List<Rss> getAllRssAvailable()
        {

            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();
                 IQueryable<Rss> list =  db.Rsses.Select(s => s);
                 return list.ToList();
            }
            catch (Exception e)
            {
                throw (new Exception("Erreur GetAllRssAvailable : " + e.Message, e));
            }
        }

        #endregion

        #region TransformationMethode

        public DataRss getDataRss(Rss rss)
        {
            if (rss == null)
                return null;
            return new DataRss
            {
                guid = rss.guid,
                id = rss.id == null ? Guid.Empty : rss.id,
                last_importation = rss.last_date_importation,
                url = rss.url,
                name = rss.name,
                link = rss.link
            };
        }

        public Rss getRssUser(DataRss rss)
        {

            if (rss == null)
                return null;
            return new Rss
            {
                guid = rss.guid,
                id = rss.id == null ? Guid.Empty : rss.id,
                last_date_importation = rss.last_importation == null ? DateTime.MinValue : (DateTime)rss.last_importation,
                url = rss.url,
                name = rss.name,
                link = rss.link
            };
        }


        public List<DataRss> getListDataRss(List<Rss> list)
        {
            List<DataRss> res = new List<DataRss>();

            foreach (Rss tmp in list)
                res.Add(this.getDataRss(tmp));
            return res;
        }

        public List<Rss> getListUserRss(List<DataRss> list)
        {
            List<Rss> res = new List<Rss>();

            foreach (DataRss tmp in list)
                res.Add(this.getRssUser(tmp));
            return res;
        }

        #endregion

        #region methodes hérités

        public override RssTree getRecursiveSubDirectories(RssTree parent)
        {

            List<DirectoriesUser> list = getListChildDirectory(parent.currentDir.id);
            parent.rssList = getListDataRss(getRssForDir(parent.currentDir.id));
            try
            {
                LinkServeurRSSDataContext db = new LinkServeurRSSDataContext();

                foreach (DirectoriesUser tmp in list)
                {
                    RssTree node = new RssTree(getDataDirectory(tmp));
                   
                    node = this.getRecursiveSubDirectories(node);
                    parent.subDir.Add(tmp.name, node);
                }
                return parent;
            }
            catch (Exception e)
            {
                return null;
            }
        }
        
        #endregion
    }
}